home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / c / intser.exe / INTSER.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-19  |  14.4 KB  |  534 lines

  1. /************************************************************************/
  2. /*                                    */
  3. /*    Minimal Function, All C interrupt serial routine        */
  4. /*                                    */
  5. /*    Supports buffered input, output                    */
  6. /*                                    */
  7. /*                                    */
  8. /************************************************************************/
  9. /*                                    */
  10. /*    This code is donated to the public domain            */
  11. /*                                    */
  12. /************************************************************************/
  13.  
  14. #include <dos.h>
  15. #include <conio.h>
  16. #include <stdlib.h>
  17.  
  18. /************************************************************************/
  19. /*                                    */
  20. /*    The following callable subroutines are in this library        */
  21. /*                                    */
  22. /************************************************************************/
  23. int     ser_int_icount();    /* Count of chars in input buffer    */
  24. int     ser_int_ocount();    /* Count of chars in output buffer    */
  25. int     ser_int_ofree();    /* Count of free space in output buffer    */
  26. void    ser_int_iflush();    /* Flushes input buffer            */
  27. void    ser_int_init();        /* Initializes interrupt handler    */
  28. void    ser_int_term();        /* Terminates interrupt handler        */
  29. void    ser_int_putc();        /* Sends a character            */
  30. int     ser_int_getc();        /* Gets a character            */
  31. void    ser_int_puts();        /* Sends a string            */
  32. void    ser_int_break();    /* Sends a break signal            */
  33.  
  34. /************************************************************************/
  35. /*    Definitions of the 8250's registers                */
  36. /************************************************************************/
  37.  
  38. unsigned    ser_vect;    /* Interrupt vector to use        */
  39. unsigned    DataPort;    /* Data register            */
  40. unsigned    IER;        /* Interrupt enable register            */
  41. unsigned    IIR;        /* Interrupt  ID register        */
  42. unsigned    LCR;        /* Line control register        */
  43. unsigned    MControl;    /* Modem control register        */
  44. unsigned    LStatus;    /* Line Status                */
  45. unsigned    MStatus;    /* Modem status                */
  46.  
  47. unsigned    IntControl;    /* For 8259 interrupt controller        */
  48. unsigned    EnableIRQ4;    /* Enable interrupt 4            */
  49. unsigned    DisableIRQ4;    /* Disable interrupt 4            */
  50. unsigned    RS8259;
  51. unsigned    EOI;
  52. unsigned    TxReady;    /* Transmit buffer empty        */
  53.  
  54. static (_interrupt _far *old_vect)();
  55.                 /* The previous interrupt vector.    */
  56.  
  57. /************************************************************************/
  58. /*    Definitions of the interrupt enable register            */
  59. /************************************************************************/
  60.  
  61. #define    IER_RX        0x01    /* Enable Data Ready interrupts        */
  62. #define    IER_TX        0x02    /* Enable Transmit Empty Interrupts    */
  63. #define IER_LS          0x04    /* Enable line status interrupts        */
  64. #define IER_MS          0x08    /* Enable modem status interrupts       */
  65.  
  66. /************************************************************************/
  67. /*    Definitions of the interrupt ID register            */
  68. /************************************************************************/
  69. #define    IIR_IPending    0x01    /* Interrupt is pending            */
  70. #define    IIR_Mask    0x06    /* Mask to get Interrupt type        */
  71. #define    IIR_MS        0x00    /* Modem status interrupt        */
  72. #define    IIR_TX        0x02    /* Transmit empty interrupt        */
  73. #define    IIR_RX        0x04    /* Data available interrupt        */
  74. #define    IIR_LS        0x06    /* Line status interrupt        */
  75.  
  76. #define BUFSIZE         512     /* How many characters in the buffer?   */
  77. #define    XOFF_SIZE    384    /* How full do we get before XOFFing    */
  78. #define    XON_SIZE    128    /* How empty to we get before XONing    */
  79. #define    XOFF        19    /* value of the XOFF character        */
  80. #define    XON        17    /* value of the XON character        */
  81.  
  82. /************************************************************************/
  83. /*                                    */
  84. /*    Definitions for the input buffer.                */
  85. /*                                    */
  86. /*    We make it a 'far' array so as not to eat up valuable data    */
  87. /*    space in the small and medium models.                */
  88. /*                                    */
  89. /*    The buffers are simple circular buffers, where there is        */
  90. /*    one pointer to fill the buffer and another to empty it.        */
  91. /*    A count variable keeps track of the size of the buffer        */
  92. /*    (although it could be determined by computing             */
  93. /*    (fill-empty)%BUFSIZE).                        */
  94. /*                                    */
  95. /************************************************************************/
  96.  
  97. static unsigned char far in_buf[BUFSIZE];
  98. static int    in_fill        =0;    /* Where to place the next char    */
  99. static int    in_empty    =0;    /* Where to get the next char    */
  100. static int      in_count    =0;    /* How much data is in buffer    */
  101. static int    xoff_sent    =0;    /* Did we send an XOFF?        */
  102.  
  103. /************************************************************************/
  104. /*                                    */
  105. /*    Definitions for the output buffer                */
  106. /*                                    */
  107. /************************************************************************/
  108.  
  109. static unsigned char far out_buf[BUFSIZE];
  110. static int    out_fill    =0;
  111. static int    out_empty    =0;
  112. static int      out_count    =0;
  113.  
  114. static int    tx_off        =1;    /* These means the interrupts    */
  115.                     /* for the transmitter are now    */
  116.                     /* off (since we have nothing    */
  117.                     /* to send right now).        */
  118.  
  119. /************************************************************************/
  120. /*                                    */
  121. /*    ser_int_icount:        Return the number of bytes in the input    */
  122. /*                buffer.  0 means no data available.    */
  123. /*                                    */
  124. /************************************************************************/
  125. int     ser_int_icount()
  126. {
  127.     return(in_count);
  128. }
  129.  
  130. /************************************************************************/
  131. /*                                    */
  132. /*    ser_int_ocount:    Returns the number of bytes in the output    */
  133. /*            buffer. This information is useful if you    */
  134. /*            are uploading a large block of data and do    */
  135. /*            not want to hand this library data quicker    */
  136. /*            than it can be transmitted.  E.g.,        */
  137. /*                                    */
  138. /*                while (more data)            */
  139. /*                {                    */
  140. /*                    while (ser_int_ocount())    */
  141. /*                        ;            */
  142. /*                    send_data();            */
  143. /*                }                    */
  144. /*                                    */
  145. /************************************************************************/
  146. int     ser_int_ocount()
  147. {
  148.     return(out_count);
  149. }
  150.  
  151. /************************************************************************/
  152. /*                                    */
  153. /*    ser_int_ofree:    Returns the amount of free space left in    */
  154. /*            the output buffer.  Useful in preventing    */
  155. /*            hanging in ser_int_putc while sending a     */
  156. /*            a large block of data.  E.g.,            */
  157. /*                                    */
  158. /*                while (more_data)            */
  159. /*                {                    */
  160. /*                    while (ser_int_ofree() == 0)    */
  161. /*                        do_something_else();    */
  162. /*                    ser_int_putc(data);        */
  163. /*                }                    */
  164. /*                                    */
  165. /************************************************************************/
  166. int     ser_int_ofree()
  167. {
  168.     return(BUFSIZE-out_count);
  169. }
  170.  
  171. /************************************************************************/
  172. /*                                    */
  173. /*    ser_int_iflush:    Throw away any data in the input buffer.    */
  174. /*            Note that if a character arrives just as    */
  175. /*            this subroutine is returning, there will    */
  176. /*            be data in the input buffer upon return.    */
  177. /*            Thus, you cannot rely upon the buffer        */
  178. /*            being empty (for whatever reason).        */
  179. /*                                    */
  180. /************************************************************************/
  181. void    ser_int_iflush()
  182. {
  183.     _disable();
  184.  
  185.         /*****************************************/
  186.             in_fill        = 0;
  187.             in_empty    = 0;
  188.             in_count    = 0;
  189.         /*****************************************/
  190.  
  191.     _enable();
  192. }
  193.  
  194. /************************************************************************/
  195. /*                                    */
  196. /*    int_handler:        This routine catches the serial port    */
  197. /*                interrupts, and performs accordingly.    */
  198. /*                                    */
  199. /************************************************************************/
  200. static void interrupt far int_handler()
  201. {
  202.     int     ax;
  203.     static int send_now = 0;
  204.  
  205.     /* While there is more work to do...                */
  206.     while (0 == ((ax = inp(IIR)) & IIR_IPending))
  207.     {
  208.         switch (ax & IIR_Mask)
  209.         {
  210.         case IIR_RX:            /* New data has arrived    */
  211.  
  212.             ax      =inp(DataPort);
  213.  
  214.             if (in_count != BUFSIZE)
  215.             {
  216.                 in_buf[in_fill] = (unsigned char) ax;
  217.                 in_fill = (in_fill+1) & (BUFSIZE-1);
  218.                 in_count++;
  219.  
  220.                 /* Check for the buffer filling up...    */
  221.                 if ((in_count == XOFF_SIZE) && (!xoff_sent))
  222.                 {
  223.                     if (tx_off)
  224.                     {
  225.                         outp(DataPort,XOFF);
  226.                         tx_off = 0;
  227.                     }
  228.                     else
  229.                     {
  230.                         send_now = XOFF;
  231.                     }
  232.                     xoff_sent = 1;
  233.                 }
  234.             }
  235.             break;
  236.  
  237.         case IIR_TX:        /* Ready to transmit another    */
  238.  
  239.             if (send_now)
  240.             {
  241.                 outp(DataPort,send_now);
  242.                 send_now = 0;
  243.             } 
  244.             else if (out_count)
  245.             {
  246.                 outp(DataPort,out_buf[out_empty]);
  247.                 out_empty = (out_empty+1) & (BUFSIZE-1);
  248.                 out_count--;
  249.             }
  250.             else
  251.             {
  252.                 /* Since we aren't feeding it another    */
  253.                 /* byte, it won't interrupt again    */
  254.                 tx_off = 1;
  255.             }
  256.             break;
  257.  
  258.         default:
  259.             break;
  260.         }
  261.     }
  262.     outp(RS8259,EOI);
  263. }
  264.  
  265.  
  266. /************************************************************************/
  267. /*                                    */
  268. /*    ser_int_init:    Initialize the serial port handler.        */
  269. /*            Note that if you change the baud rate        */
  270. /*            via the ROM bios (or MSC's _bios* funcs)    */
  271. /*            for example, you need to call this routine    */
  272. /*            again to restart the interrupts.        */
  273. /*                                    */
  274. /*    The argument, com_port, should be either 1 or 2            */
  275. /************************************************************************/
  276. static int      been_inited = 0;
  277. void ser_int_term(void);
  278.  
  279. void    ser_int_init(int com_port)
  280. {
  281.     register int ax;
  282.     register int i;
  283.  
  284.     if (been_inited)
  285.     {
  286.         ser_int_term();
  287.     }
  288.     else
  289.     {
  290.         onexit(ser_int_term);
  291.         been_inited = 1;
  292.     }
  293.  
  294.     if (com_port == 2)
  295.     {
  296.         ser_vect    =0xb;
  297.         DataPort    =0x2f8;
  298.         IER        =0x2f9;
  299.         IIR        =0x2fa;
  300.         LCR        =0x2fb;
  301.         MControl    =0x2fc;
  302.         LStatus        =0x2fd;
  303.         MStatus        =0x2fe;
  304.  
  305.         IntControl    =0x21;
  306.         EnableIRQ4    =0xF7;
  307.         DisableIRQ4    =0x08;
  308.         RS8259        =0x20;
  309.         EOI        =0x20;
  310.         TxReady        =0x20;
  311.     }
  312.     else    /* COM 1 */
  313.     {
  314.         ser_vect    =0xc;
  315.         DataPort    =0x3f8;
  316.         IER        =0x3f9;
  317.         IIR        =0x3fa;
  318.         LCR        =0x3fb;
  319.         MControl    =0x3fc;
  320.         LStatus        =0x3fd;
  321.         MStatus        =0x3fe;
  322.  
  323.         IntControl    =0x21;
  324.         EnableIRQ4    =0xef;
  325.         DisableIRQ4    =0x10;
  326.         RS8259        =0x20;
  327.         EOI        =0x20;
  328.         TxReady        =0x20;
  329.     }
  330.  
  331.     _disable();
  332.  
  333.     old_vect = _dos_getvect(ser_vect);
  334.  
  335.     _dos_setvect(ser_vect,int_handler);
  336.  
  337.     _disable();
  338.  
  339.         /*****************************************/
  340.             ax      =inp(IntControl);
  341.             ax      =ax & EnableIRQ4;
  342.             outp(IntControl,ax);
  343.  
  344.             outp(IER,IER_RX | IER_TX);
  345.  
  346.             for (i=0;i<6;i++)
  347.                 inp(DataPort+i);
  348.  
  349.             ax      =inp(LCR);
  350.             ax      =ax & 0x3f;
  351.             outp(LCR,ax);
  352.  
  353.             outp(MControl,0xb);
  354.  
  355.             outp(RS8259,EOI);
  356.         /*****************************************/
  357.  
  358.     _enable();
  359. }
  360.  
  361. /************************************************************************/
  362. /*                                    */
  363. /*    ser_int_term:        Turn off the interrrupts to make the    */
  364. /*                world safe for us to exit...        */
  365. /*                                    */
  366. /************************************************************************/
  367. void ser_int_term()
  368. {
  369. #if     0
  370.     /*      Wait for all data in the buffer to be transmitted       */
  371.     while (out_count)
  372.         {}
  373.  
  374.     /*      Wait for the UART to drain out                          */
  375.     while ((inp(LStatus) & 0x60) != 0x60)
  376.         {}
  377. #endif
  378.  
  379.     _disable();
  380.     /*****************************************/
  381.  
  382.         outp(IntControl,inp(IntControl) | DisableIRQ4);
  383.         outp(LCR,inp(LCR) & 0x7f);
  384.         outp(IER,0);
  385.         outp(MControl,0);
  386.  
  387.     /*****************************************/
  388.     _enable();
  389.  
  390.     _dos_setvect(ser_vect,old_vect);
  391. }
  392.  
  393. /************************************************************************/
  394. /*                                    */
  395. /*    ser_int_putc:        Transmit a character out the UART    */
  396. /*                                    */
  397. /************************************************************************/
  398. void    ser_int_putc(int c)
  399. {
  400.     _disable();
  401.  
  402.         /*****************************************/
  403.  
  404.             /* If there is nothing ahead of us, just send it! */
  405.             if (tx_off)
  406.             {
  407.                 outp(DataPort,c);
  408.                 tx_off = 0;
  409.             }
  410.             else    /* Put character in the buffer        */
  411.             {
  412.                 if (out_count == BUFSIZE)
  413.                 {
  414.                 _enable();
  415.                 /*****************************************/
  416.                     /* Buffer is full, wait for space */
  417.                     while (out_count == BUFSIZE)
  418.                         ;
  419.                 /*****************************************/
  420.                 _disable();
  421.             }
  422.             out_buf[out_fill] = (unsigned char) c;
  423.             out_fill = (out_fill + 1) & (BUFSIZE-1);
  424.             out_count++;
  425.             }
  426.         /*****************************************/
  427.  
  428.     _enable();
  429. }
  430.  
  431.  
  432. /************************************************************************/
  433. /*                                    */
  434. /*    ser_int_getc:    Get a character from the input buffer.        */
  435. /*            Returns character or -1 if no data available    */
  436. /*                                    */
  437. /************************************************************************/
  438. int     ser_int_getc()
  439. {
  440.     int     b;
  441.  
  442.     if (in_count == 0)
  443.         return(-1);
  444.  
  445.     b       =in_buf[in_empty];
  446.     in_empty        = (in_empty+1) & (BUFSIZE-1);
  447.  
  448.     _disable();
  449.  
  450.         /*****************************************/
  451.             in_count--;
  452.         /*****************************************/
  453.  
  454.     _enable();
  455.  
  456.     if ((in_count == XON_SIZE) && xoff_sent)
  457.     {
  458.         ser_int_putc(XON);
  459.         xoff_sent = 0;
  460.     }
  461.     return(b);
  462. }
  463.  
  464. /************************************************************************/
  465. /*                                    */
  466. /*    ser_int_puts:        Transmit a string of characters        */
  467. /*                                    */
  468. /************************************************************************/
  469. void ser_int_puts(char *s)
  470. {
  471.     while (*s)
  472.         ser_int_putc(*s++);
  473. }
  474.  
  475.  
  476. /************************************************************************/
  477. /*                                    */
  478. /*    ser_int_break:    Raise BREAK for 200 ms, then turn it off    */
  479. /*            for 200 ms.                    */
  480. /*                                    */
  481. /*    This routine is structured so that you can call it repeatedly    */
  482. /*    and the other end will sense distinct break signals rather    */
  483. /*    than one very long one.                        */
  484. /*                                    */
  485. /************************************************************************/
  486. void ser_int_break()
  487. {
  488.     int    ax;
  489.     unsigned long far *ticks;
  490.  
  491.     unsigned long start, cur;
  492.  
  493.     FP_SEG(ticks) = 0x40;
  494.     FP_OFF(ticks) = 0x6c;
  495.  
  496.     start   =*ticks;
  497.  
  498.     _disable();
  499.  
  500.         /*****************************************/
  501.         /*    Turn on BREAK signal         */
  502.         /*****************************************/
  503.             ax    =inp(LCR);
  504.             ax    |= 0x40;
  505.             outp(LCR,ax);
  506.     _enable();
  507.  
  508.     /*    Wait for 200 ms        */
  509.     while (1)
  510.     {
  511.         cur     =*ticks;
  512.         if ((cur-start) > 4)    /* 220 uSecs    */
  513.             break;
  514.     }
  515.  
  516.     _disable();
  517.         /*****************************************/
  518.         /*    Turn off BREAK signal         */
  519.         /*****************************************/
  520.             ax      =inp(LCR);
  521.             ax      &= ~0x40;
  522.             outp(LCR,ax);
  523.     _enable();
  524.  
  525.     start   =*ticks;
  526.  
  527.     while (1)
  528.     {
  529.         cur     =*ticks;
  530.         if ((cur-start) > 4)
  531.             break;
  532.     }
  533. }
  534.